package gobase

import (
	"bufio"
	"crypto/md5"
	"errors"
	"fmt"
	"io"
	"math"
	"os"
	"os/exec"
	"path"
	"path/filepath"
	"regexp"
	"runtime"
	"strings"
	"time"
)

var (
	PathSeparator        = "\\"
	RootPath             = ""
	cmd_exe_bin   string = ""
)

var zeroTime time.Time

type DiskStatus struct {
	All  uint64 `json:"all"`
	Used uint64 `json:"used"`
	Free uint64 `json:"free"`
}

var (
	SIZE_KB uint64 = 1024
	SIZE_MB        = 1024 * SIZE_KB
	SIZE_GB        = 1024 * SIZE_MB
	SIZE_TB        = 1024 * SIZE_GB
)

/*
*
返回小数 0.1  = 10%
*/
func (this DiskStatus) FreePercent() float64 {
	return float64(this.Free) / float64(this.All)
}

func (this DiskStatus) FreePercentStr(n int) string {
	fmtstr := "%." + fmt.Sprintf("%d", n) + "f %%"
	return fmt.Sprintf(fmtstr, this.FreePercent()*100)
}

//var (
//	SIZE_KB uint64 = 1024
//	SIZE_MB        = 1024 * SIZE_KB
//	SIZE_GB        = 1024 * SIZE_MB
//	SIZE_TB        = 1024 * SIZE_GB
//)

/*
**

	SizeStrToSize("1024MB") SizeStrToSize("1024B")
*/
func SizeStrToSize(str string) int64 {
	num := 1
	mult := 1024
	if len(str) < 1 {
		return StrToInt64Def(str, 0)
	}
	if len(str) > 1 {
		chr := str[len(str)-1]
		if chr == 'B' || chr == 'b' {
			str = str[:len(str)-1]
		}

		if len(str) < 1 {
			return StrToInt64Def(str, 0)
		}

		chr = str[len(str)-1]

		switch chr {
		case 'G', 'g':
			num *= mult
			fallthrough
		case 'M', 'm':
			num *= mult
			fallthrough
		case 'K', 'k':
			num *= mult
			str = str[0 : len(str)-1]
		default:
			return StrToInt64Def(str, 0)
		}
	}
	v1 := StrToFloat64Def(str, 0)
	return int64(v1 * float64(num))
}

func Size2GB(sizebytes uint64) float64 {
	return float64(sizebytes) / float64(SIZE_GB)

}

func HumanFilesizeU(sizebytes uint64) string {
	osize := sizebytes

	rval := ""

	t := float64(sizebytes) / float64(SIZE_TB)
	if t > 1 {
		rval = fmt.Sprintf("%.2f TB ", t)
		return rval
	}

	t = float64(sizebytes) / float64(SIZE_GB)
	if t > 1 {
		rval = fmt.Sprintf("%.2f GB ", t)
		return rval
	}

	t = float64(sizebytes) / float64(SIZE_MB)
	if t > 1 {
		rval = fmt.Sprintf("%.2f MB ", t)
		return rval
	}

	t = float64(sizebytes) / float64(SIZE_KB)
	if t > 1 {
		rval = fmt.Sprintf("%.2f KB ", t)
		return rval
	}

	return fmt.Sprintf("%d B", osize)
}

func HumanFilesize(sizebytes int64) string {
	return HumanFilesizeU(uint64(sizebytes))
}

// ReadDir reads the directory named by dirname and returns
// a list of directory without sort.
func ReadDir(dirname string) ([]os.FileInfo, error) {
	f, err := os.Open(dirname)
	if err != nil {
		return nil, err
	}
	list, err := f.Readdir(-1)
	f.Close()
	if err != nil {
		return nil, err
	}
	return list, nil
}

/*
**

	onerr 返回false 则返回退出当前目录的查找
*/
func searchAFile(path string, cmpfilepath *string, cmpfile *os.FileInfo, currlevel, maxlevel int, comparefunc func(cmp, cur os.FileInfo, mpath, cpath string) bool, onerr func(dirname string, err error) bool) error {
	if currlevel >= maxlevel {
		return nil
	}
	files, err := ReadDir(path)
	if err != nil {
		return err
	}
	for _, itm := range files {
		if itm.IsDir() {
			dirname := path + itm.Name() + PathSeparator
			err := searchAFile(dirname, cmpfilepath, cmpfile, currlevel+1, maxlevel, comparefunc, onerr)
			if err != nil && onerr != nil {
				if !onerr(dirname, err) {
					break
				}
			}
		} else {
			if comparefunc(*cmpfile, itm, *cmpfilepath, path) {
				*cmpfile = itm
				*cmpfilepath = path
			}
		}
	}
	return nil
}

func searchFiles(path string, afilepath *string, afile os.FileInfo, currlevel, maxlevel int, cbfunc func(m, c os.FileInfo, mpath, cpath string) bool) error {
	if currlevel >= maxlevel {
		return nil
	}
	files, err := ReadDir(path)
	if err != nil {
		return err
	}
	for _, itm := range files {
		if itm.IsDir() {
			if !cbfunc(afile, itm, *afilepath, path) {
				break
			}
			err := searchFiles(path+itm.Name()+PathSeparator, afilepath, afile, currlevel+1, maxlevel, cbfunc)
			if err != nil {
				return err
			}
		} else {
			if !cbfunc(afile, itm, *afilepath, path) {
				break
			}
		}
	}
	return nil
}

/*
**

	Range path下的文件和目录
	maxlevel 表示目录层级
	  0: 表示搜索所有
	  1:表示获取当前目录(不会进入下一级目录)
	fn: 如果返回 false 表示退出当前目录搜索, path为当前文件所在路径
	onerr 返回false 则返回退出当前目录的range
*/
func RangeFiles(path string, maxlevel int, fn func(path string, file os.FileInfo) bool, onerr func(dirname string, err error) bool) error {
	return rangeFiles(EnsurePathWithSlash(path, PathSeparator), 0, maxlevel, fn, onerr)
}

/*
**

	onerr 返回false 则返回退出当前目录的range
*/
func rangeFiles(path string, currlevel, maxlevel int, fn func(path string, file os.FileInfo) bool, onerr func(dirname string, err error) bool) error {
	if currlevel >= maxlevel && maxlevel > 0 {
		return nil
	}
	files, err := ReadDir(path)
	if err != nil {
		return err
	}
	for _, itm := range files {
		if fn != nil {
			if !fn(path, itm) {
				break
			}
		}
		if itm.IsDir() {
			dirname := path + itm.Name() + PathSeparator
			err := rangeFiles(dirname, currlevel+1, maxlevel, fn, onerr)
			if err != nil && onerr != nil {
				if !onerr(dirname, err) {
					break
				}
			}
		}
	}
	return nil
}

/*
1. 如果文件已经存在, 则对之前的文件进行更名
*/
func CheckRotateFile(filename string) error {
	_, err := os.Lstat(filename)
	if err == nil { // file exists
		// Find the next available number
		num := 1
		fname := ""

		// xx.2->xx.3, xx.1 -> xx.2
		err_cnt := 0
		var last_err error = nil
		num = 100 - 1
		for ; num >= 1; num-- {
			fname = filename + fmt.Sprintf(".%d", num)
			nfname := filename + fmt.Sprintf(".%d", num+1)
			_, err = os.Lstat(fname)
			if err == nil {
				err = os.Rename(fname, nfname)
				if err != nil {
					err_cnt++
					last_err = err
				}
			}
		}

		if last_err != nil {
			fmt.Fprintf(os.Stderr, "[%s]CheckRotateFile, errcnt:%d, last err:%v\r\n", NowString(), err_cnt, err)
		}

		err = os.Rename(filename, fname)
		if err != nil {
			return fmt.Errorf("CheckRotateFile Rename: %s\n", err)
		}
	}
	return nil
}

/*
 1. 如果文件已经存在, 则对之前的文件进行更名, 更名失败 返回异常
 2. 更名成功则创建新文件的os.file. 创建成功则返回, 失败,进行重试, 超过10次返回异常
    创建的文件成功会被清空
*/
func CheckRotateFileAfterCreateFile(filename string) (fn *os.File, err error) {
	j := 0
	for {
		err := CheckRotateFile(filename)
		if err != nil {
			return nil, err
		}

		fn, e1 := os.OpenFile(filename, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0666)
		if e1 == nil {
			return fn, nil
		} else if j >= 10 {
			return nil, e1
		}
		j++
	}
	return nil, nil // 应该不会到这一步
}

func CopyFile(src, dst string) (w int64, err error) {
	srcFile, err := os.Open(src)
	if err != nil {
		return
	}
	defer srcFile.Close()
	dstFile, err := os.OpenFile(dst, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0666)
	if err != nil {
		return
	}
	defer dstFile.Close()
	return io.Copy(dstFile, srcFile)
}

func SearchFiles(path string, currlevel, maxlevel int, comparefunc func(m, c os.FileInfo, mpath, cpath string) bool) (string, error) {
	var afile os.FileInfo
	var passPath string
	err := searchFiles(path, &passPath, afile, currlevel, maxlevel, comparefunc)
	if afile == nil {
		return "", err
	} else {
		return fmt.Sprintf("%s%s", passPath, afile.Name()), err
	}
}

/**
 *  查找符合条件的第一个文件
 *    comparefunc 返回true表示 cur比cmp文件更符合条件. cmp可能为空表示还没有查找到符合条件的文件, 判断时需要注意
 */
func SearchAFile(path string, currlevel, maxlevel int, comparefunc func(cmp, cur os.FileInfo, cmppath, curpath string) bool, onerr func(dirname string, err error) bool) (string, error) {
	var afile os.FileInfo
	var passPath string
	if !strings.HasSuffix(path, PathSeparator) {
		path += PathSeparator
	}
	err := searchAFile(path, &passPath, &afile, currlevel, maxlevel, comparefunc, onerr)
	if afile == nil {
		return "", err
	} else {
		return fmt.Sprintf("%s%s", passPath, afile.Name()), err
	}
}

/**
 *  查找符合条件的第一个文件
 *    comparefunc 返回true表示 cur比cmp文件更符合条件. cmp可能为空表示还没有查找到符合条件的文件, 判断时需要注意
 */
func SearchAFileEx(path string, currlevel, maxlevel int, comparefunc func(cmp, cur os.FileInfo, cmppath, curpath string) bool, onerr func(dirname string, err error) bool) (string, os.FileInfo) {
	var afile os.FileInfo
	var passPath string
	if !strings.HasSuffix(path, PathSeparator) {
		path += PathSeparator
	}
	searchAFile(path, &passPath, &afile, currlevel, maxlevel, comparefunc, onerr)
	if afile == nil {
		return "", nil
	} else {
		return passPath, afile
	}
}

func ExtractFilePath(fileName string) string {
	fileName = strings.Replace(fileName, "\\", "/", -1)
	return path.Dir(fileName)
}

/*
*

	路径最后带分隔符
*/
func Split2PathAndFileName(fileName string) (dir string, name string) {
	if strings.ContainsRune(fileName, '\\') {
		fileName = strings.ReplaceAll(fileName, "\\", "/")
		dir, name = path.Split(fileName)
		dir = strings.ReplaceAll(dir, "/", "\\")
		return
	} else {
		return path.Split(fileName)
	}

}

/*
**

	带path分隔符
*/
func GetCurrentWorkingPath() (path string) {
	path, _ = os.Getwd()
	if len(path) > 0 {
		path += PathSeparator
	}
	return
}

func GetCurrentDirectory() (path string, err error) {
	dir, err := filepath.Abs(filepath.Dir(os.Args[0])) //返回绝对路径  filepath.Dir(os.Args[0])去除最后一个元素的路径
	return dir, err
}

/**
 * 最后不带路径符
 */
func GetExecuteFileDirectory() (path string) {
	dir, _ := filepath.Abs(filepath.Dir(os.Args[0])) //返回绝对路径  filepath.Dir(os.Args[0])去除最后一个元素的路径
	return dir
}

/*
*

	返回执行文件名
*/
func GetExecuteFileName() (name string) {
	return ExtractFileName(os.Args[0]) // 返回执行文件名
}

// get file modified time
func FileMTime(fp string) (time.Time, error) {
	f, e := os.Stat(fp)
	if e != nil {
		return zeroTime, e
	}
	return f.ModTime(), nil
}

// get file size as how many bytes
func FileSize(fp string) (int64, error) {
	f, e := os.Stat(fp)
	if e != nil {
		return 0, e
	}
	return f.Size(), nil
}

func PathBase(path string) string {
	if path == "" {
		return "."
	}
	// Strip trailing slashes.
	for len(path) > 0 && (path[len(path)-1] == '/' || path[len(path)-1] == '\\') {
		path = path[0 : len(path)-1]
	}

	i := strings.LastIndexByte(path, '/')
	j := strings.LastIndexByte(path, '\\')

	if j > i {
		i = j
	}

	if i >= 0 {
		path = path[i+1:]
	}
	// If empty now, it had only slashes.
	if path == "" {
		return "/"
	}
	return path

}

func DeleteFileNameExt(fileName string) string {
	fileExt := path.Ext(fileName)
	rval := strings.TrimSuffix(fileName, fileExt)
	return rval
}

/*
*

	移除 文件或者文件夹
*/
func DeleteFileOrDir(strPath string) error {
	return os.RemoveAll(strPath)
}

/*
*

	"W:\\temp\\测试删除文件夹\\a.txt"  -> a.txt
*/
func ExtractFileName(fileName string) string {
	rval := PathBase(fileName)
	return rval
}

func ExtractFileNameWithOutPathAndExt(fileName string) string {
	rval := PathBase(fileName)
	fileExt := path.Ext(rval)
	rval = strings.TrimSuffix(rval, fileExt)
	return rval
}

/*
**
如果返回的错误为nil,说明文件或文件夹存在
如果返回的错误类型使用os.IsNotExist()判断为true,说明文件或文件夹不存在
如果返回的错误为其它类型,则不确定是否在存在
*/
func PathExists(path string) (bool, error) {
	_, err := os.Stat(path)
	if err == nil {
		return true, nil
	}
	if os.IsNotExist(err) {
		return false, nil
	}
	return false, err
}

func ForceCreatePath(strPath string) bool {
	err := os.MkdirAll(strPath, 0777)
	if err == nil {
		return true
	} else {
		return os.IsExist(err)
	}
}

func ForceAllPath(strPath string) error {
	err := os.MkdirAll(strPath, 0777)
	if err == nil {
		return nil
	} else if os.IsExist(err) {
		return nil
	} else {
		return err
	}
}

func ForceCreateFilePath(fileName string) error {
	str := ExtractFilePath(fileName)
	bv, _ := PathExists(str)
	if !bv {
		err := os.MkdirAll(str, 0777)
		return err
	} else {
		return nil
	}
}

func ExecCmdNoWait(cmdLine string) (*exec.Cmd, error) {
	args := strings.Split(cmdLine, " ")
	cmd := exec.Command(args[0], args[1:]...)
	cmd.Stdin = os.Stdin
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	err := cmd.Start()
	return cmd, err
}

func ExecCmdNoWaitNoSettingIO(cmdLine string) (*exec.Cmd, error) {
	args := strings.Split(cmdLine, " ")
	cmd := exec.Command(args[0], args[1:]...)
	err := cmd.Start()
	return cmd, err
}

func ExecCmdLine(cmdLine string, arg_spliter string) error {
	args := strings.Split(cmdLine, arg_spliter)
	cmd := exec.Command(args[0], args[1:]...)
	cmd.Stdin = os.Stdin
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	err := cmd.Run()
	return err
}

/***
 * exeshell("del *.log")
 */
func ExecShell(cmdLine string) error {
	var cmd *exec.Cmd
	if runtime.GOOS == "windows" {
		cmd = exec.Command("cmd", "/C", cmdLine)
	} else {
		cmd = exec.Command(cmd_exe_bin, "-c", cmdLine)
	}
	return cmd.Run()
}

func GetExecShellCmd(cmdLine string) *exec.Cmd {
	var cmd *exec.Cmd
	if runtime.GOOS == "windows" {
		cmd = exec.Command("cmd", "/C", cmdLine)
	} else {
		cmd = exec.Command(cmd_exe_bin, "-c", cmdLine)
	}
	return cmd
}

/***
 * exec("perl xxx *.log")
 */
func ExecWithStdIO(cmdLine string) error {
	cmd := exec.Command(cmdLine)
	cmd.Stdin = os.Stdin
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	return cmd.Run()
}

/***
 * exeshell("del *.log")
 */
func ExecShellWithStdIO(cmdLine string) error {
	var cmd *exec.Cmd
	if runtime.GOOS == "windows" {
		cmd = exec.Command("cmd", "/C", cmdLine)
	} else {
		cmd = exec.Command(cmd_exe_bin, "-c", cmdLine)
	}
	cmd.Stdin = os.Stdin
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	return cmd.Run()
}

func ExecShellOutput(cmd string) (err error, str string) {
	var sb strings.Builder
	var errsb strings.Builder
	e1 := ExecShellWithIO(cmd, nil, &sb, &errsb)
	if e1 != nil {
		return fmt.Errorf("异常:%s, 错误输出:%s", e1.Error(), errsb.String()), ""
	} else {
		return nil, sb.String()
	}
}

/*
*
sb := NewBytesBuilder()
ExecShellWithIO("dir c:\\*.*", nil, sb, sb)
sb.StatusString();
*/
func ExecShellWithIO(cmdLine string, rd io.Reader, outstd, outerr io.Writer) error {
	var cmd *exec.Cmd
	if runtime.GOOS == "windows" {
		cmd = exec.Command("cmd", "/C", cmdLine)
	} else {
		cmd = exec.Command(cmd_exe_bin, "-c", cmdLine)
	}
	if rd != nil {
		cmd.Stdin = rd
	}
	cmd.Stdout = outstd
	cmd.Stderr = outerr
	return cmd.Run()
}

func ExecShellAndNoWait(cmdLine string) error {
	var cmd *exec.Cmd
	if runtime.GOOS == "windows" {
		cmd = exec.Command("cmd", "/C", cmdLine)
	} else {
		cmd = exec.Command(cmd_exe_bin, "-c", cmdLine)
	}
	return cmd.Start()
}

/**
 * 如果filename存在，
 *     尝试对filename进行重命名, 然后创建一个新的file
 *     如果重命名失败，则使用一个新的文件名
 */
func TryCreateFileAndRenameIfFileExists(fileName string) (newfile *os.File, ret_err error) {
	err := ForceCreateFilePath(fileName)
	if err != nil {
		return nil, err
	}

	// 如果文件存在则尝试改名
	if FileIsExists(fileName) {
		strNewName := GetNewFileUseIncSN(fileName)
		err := RenameFile(fileName, strNewName)
		if err != nil {
			ret_err = err
		}
	}

	// 如果文件还是存在，就使用新的文件
	f, _, err := CreateIfExistsThenRename(fileName) // 如果重命名旧文件失败，就用新文件
	if err != nil {
		return nil, err
	}
	newfile = f
	return
}

func CreateRewrite(fileName string) (*os.File, error) {
	return os.OpenFile(fileName, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0666)
}

func RewriteFile(fileName string, fileData []byte) error {
	return os.WriteFile(fileName, fileData, 0666)
}

func RewriteFileFunc(fileName string, fn func(w *os.File) error) error {
	f, err := os.OpenFile(fileName, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0666)
	if err != nil {
		return err
	}
	defer f.Close()
	return fn(f)
}

func ReadPerLineFile(filename string, offset int64, cb func(idx int64, line []byte) bool) (int64, error) {
	var readf *os.File
	var err error

	defer func() {
		if readf != nil {
			readf.Close()
		}
	}()

	readf, err = os.Open(filename)
	if err != nil {
		return 0, err
	}
	if offset > 0 {
		readf.Seek(offset, 0)
	}
	return ReadPerLine(readf, cb)
}

func ReadPerLine(reader io.Reader, cb func(idx int64, line []byte) bool) (int64, error) {
	br := bufio.NewReaderSize(reader, 1096)
	index := int64(0)
	for {
		lineBuf, _, err := br.ReadLine()
		if err == io.EOF {
			break
		}
		if err != nil {
			return index, err
		}

		if !cb(index, lineBuf) {
			break
		}
		index++
	}
	return index, nil
}

func ReadPerLineEx(reader io.Reader, cb func(idx int64, line []byte) bool) (int64, error) {
	br := bufio.NewReaderSize(reader, 4096)
	index := int64(0)
	lineBuf := make([]byte, 0, 8192)
	for {
		tmpBuf, isPrefix, err := br.ReadLine()
		if err == io.EOF {
			if len(lineBuf) > 0 {
				cb(index, lineBuf)
			}
			break
		}
		if err != nil {
			if len(lineBuf) > 0 {
				cb(index, lineBuf)
			}
			return index, err
		}
		lineBuf = append(lineBuf, tmpBuf...)
		if !isPrefix {
			if !cb(index, lineBuf) {
				break
			}
			index++
			lineBuf = lineBuf[:0]
		}

	}
	return index, nil
}

/***
 */
func ReadFileBlockBufOffset(fileName string, offset int64, cb func(block []byte) bool, makebuffunc func() []byte) (int64, error) {
	var readf *os.File
	var err error

	defer func() {
		if readf != nil {
			readf.Close()
		}
	}()

	readf, err = os.Open(fileName)
	if err != nil {
		return 0, err
	}

	if offset > 0 {
		readf.Seek(offset, 0)
	}

	br := bufio.NewReader(readf)
	n := int64(0)
	for {
		buf := makebuffunc()
		l, err := br.Read(buf)
		if err == io.EOF {
			break
		}
		if err != nil {
			return n, err
		}

		if !cb(buf[:l]) {
			break
		}
		n += int64(l)
	}
	return n, nil
}

/***
 */
func ReadFileBlockBuf(fileName string, cb func(block []byte) bool, makebuffunc func() []byte) (int64, error) {
	return ReadFileBlockBufOffset(fileName, 0, cb, makebuffunc)
}

func SplitStrWidth(s string, width int) (rval []string) {
	sr := strings.NewReader(s)
	val := make([]byte, width)
	for {
		n, err := sr.Read(val)
		if err != nil {
			break
		}
		rval = append(rval, string(val[:n]))
	}
	return
}

/***
 * 依次读取行, 执行cb函数
 *   cb函数如果返回false则中断读取, idx从0开始
 * 返回读取行数和错误
 */
func ReadFileLine(fileName string, cb func(idx int64, line []byte) bool) (int64, error) {
	var readf *os.File
	var err error

	defer func() {
		if readf != nil {
			readf.Close()
		}
	}()

	readf, err = os.Open(fileName)
	if err != nil {
		return 0, err
	}
	return ReadPerLineEx(readf, cb)
}

func InsertBeforeLineFile(fileName string, idx_before_line int64, fileData []byte) error {
	var old, newf *os.File
	var err error

	defer func() {
		if old != nil {
			old.Close()
		}

		if newf != nil {
			newf.Close()
		}
	}()

	old, err = os.Open(fileName)
	if err != nil {
		return err
	}
	strNewFn := fileName + ".tmp"
	newf, err = os.OpenFile(strNewFn, os.O_RDWR|os.O_CREATE, 0766)
	if err != nil {
		return err
	}
	defer newf.Close()
	br := bufio.NewReader(old)
	index := int64(1)
	for {
		if idx_before_line == index {
			_, err = newf.Write(fileData)
			if err != nil {
				return err
			}
		}

		strLine, _, err := br.ReadLine()
		if err == io.EOF {
			break
		}
		if err != nil {
			return err
		}

		_, err = newf.WriteString(string(strLine) + "\n")
		if err != nil {
			return err
		}
		index++
	}

	old.Close()
	old = nil
	newf.Close()
	newf = nil

	os.Remove(fileName)
	os.Rename(strNewFn, fileName)

	return nil
}

/*
*
  - srcFile : 存的文件
    dataFile 数据文件
    将dataFile 加入到srcFile中去
*/
func AppendAllFile(srcFile string, dataFile string) error {
	var srcF, dataF *os.File
	var err error

	defer func() {
		if srcF != nil {
			srcF.Close()
		}

		if dataF != nil {
			dataF.Close()
		}
	}()

	dataF, err = os.Open(dataFile)
	if err != nil {
		return err
	}
	srcF, err = os.OpenFile(srcFile, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
	if err != nil {
		return err
	}
	//dataR := bufio.NewReader(dataF);
	cacheBuf := make([]byte, 4096)
	for {
		n, err := dataF.Read(cacheBuf)
		if err == io.EOF {
			break
		} else if err != nil {
			return err
		}

		_, err = srcF.Write(cacheBuf[:n])
		if err != nil {
			return err
		}
	}

	return nil
}

func NewFileFromReader(fileName string, reader io.Reader) (int, error) {
	fl, err := os.OpenFile(fileName, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0666)
	if err != nil {
		return 0, err
	}
	defer fl.Close()
	cacheBuf := make([]byte, 1024)
	ncounter := 0
	for {
		n, err := reader.Read(cacheBuf)
		if err == io.EOF {
			return ncounter, nil
		}
		if err != nil {
			return ncounter, err
		}

		n, err = fl.Write(cacheBuf[:n])
		if err != nil {
			return ncounter, err
		}
		ncounter += n
	}
	return ncounter, nil
}

func NewFileData(fileName string, fileData []byte) (int, error) {
	fl, err := os.OpenFile(fileName, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0666)
	if err != nil {
		return 0, err
	}
	defer fl.Close()
	n, err := fl.Write(fileData)
	if err == nil && n < len(fileData) {
		err = io.ErrShortWrite
	}
	return n, err
}

func AppendFileEx(fileName string, fileData []byte, forceDir bool) (int, error) {
	if forceDir {
		err := ForceCreateFilePath(fileName)
		if err != nil {
			return 0, err
		}
	}
	return AppendFile(fileName, fileData)
}

func AppendFile(fileName string, fileData []byte) (int, error) {
	fl, err := os.OpenFile(fileName, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
	if err != nil {
		return 0, err
	}
	defer fl.Close()
	n, err := fl.Write(fileData)
	if err == nil && n < len(fileData) {
		err = io.ErrShortWrite
	}
	return n, err
}

func ReadFile(fileName string) ([]byte, error) {
	return os.ReadFile(fileName)
}

/*
*

	获取绝对路径
*/
func GetAbsPath(basePath, relaPath string) (string, error) {
	if len(basePath) == 0 {
		return "", errors.New("绝对路径不可以为空")
	}

	basePath = strings.Replace(basePath, "\\", "/", -1)
	relaPath = strings.Replace(relaPath, "\\", "/", -1)
	basePath = strings.TrimSuffix(basePath, "/")

	arr1 := strings.Split(basePath, "/")
	arr2 := strings.Split(relaPath, "/")
	depth := 0
	arr2idx := 0
	for i := 0; i < len(arr2); i++ {
		if arr2[i] == ".." {
			depth++
			arr2idx++
		} else if arr2[i] == "." {
			arr2idx++
		} else {
			break
		}
	}

	if arr2idx == 0 {
		rval := strings.Join(arr2[arr2idx:], PathSeparator)
		return rval, nil
	}

	if depth >= len(arr1) {
		return "", errors.New("相对路径的层数超出了范围!")
	}

	rval := strings.Join(arr1[0:len(arr1)-depth], PathSeparator)
	if arr2idx < len(arr2) {
		rval += PathSeparator + strings.Join(arr2[arr2idx:], PathSeparator)
	}
	return rval, nil
}

/*
**

	求两个绝对路径的相对路径
	  写一个函数计算出两个绝对路径（如path1 = /a/b/c/d, path2 = /a/e/f）的相对路径为 ../../e/f
*/
func GetRelativePath(basePath, path2 string) (string, error) {
	if basePath == "" || path2 == "" {
		return "", errors.New("绝对路径不可以为空")
	}
	basePath = strings.Replace(basePath, "\\", "/", -1)
	path2 = strings.Replace(path2, "\\", "/", -1)

	arr1 := strings.Split(basePath[1:], "/")
	arr2 := strings.Split(path2[1:], "/")
	depth := 0
	for i := 0; i < len(arr1) && i < len(arr2); i++ {
		if arr1[i] == arr2[i] {
			depth++
		} else {
			break
		}
	}
	prefix := ""
	if len(arr1)-depth-1 <= 0 {
		prefix = "./"
	} else {
		for i := len(arr1) - depth - 1; i > 0; i-- {
			prefix += "../"
		}
	}
	fmt.Println(depth)
	if len(arr2)-depth > 0 {
		prefix += strings.Join(arr2[depth:], "/")
	}
	return prefix, nil
}

func CombineFilePath(parts ...string) string {
	s := strings.Builder{}
	i := 0
	for _, v := range parts {
		if len(v) > 0 {
			if i > 0 {
				s.WriteString(PathSeparator)
			}
			s.WriteString(v)
			i++
		}
	}
	return s.String()
}

/*
**

	0: 不存在
	1: 文件
	2: 目录
*/
func FileIs(fileName string) int {
	if fi, _ := os.Stat(fileName); fi != nil {
		if fi.IsDir() {
			return 2
		} else {
			return 1
		}
	} else {
		return 0
	}
}

// exists returns whether the given file or directory exists or not
func FileIsExists(fileName string) bool {
	if _, err := os.Stat(fileName); err != nil {
		if os.IsNotExist(err) {
			// file does not exist
		} else {
			// other error
		}
		return false
	} else {
		//exist
		return true
	}
}

/*
*

	XXXX.dat -> XXX.1.dat(XXX.2.dat)
*/
func GetNewFileUseIncSN(fileName string) string {
	rval := fileName
	sn := 0
	var basePath string
	var fileExt string
	var baseName string
	basePath = ExtractFilePath(fileName) + "/"
	fileExt = path.Ext(fileName)
	baseName = ExtractFileNameWithOutPathAndExt(fileName)

	for {
		sn++
		rval = basePath + baseName + fmt.Sprintf(".%d%s", sn, fileExt)
		if !FileIsExists(rval) {
			return rval
		}
	}
}

func FileNameAddfix(fileName, prefix, suffix string) string {
	basePath, baseName := Split2PathAndFileName(fileName)
	fileExt := path.Ext(baseName)
	baseName = strings.TrimSuffix(baseName, fileExt)
	if len(prefix) > 0 {
		baseName = prefix + baseName
	}
	if len(suffix) > 0 {
		baseName = baseName + suffix
	}
	return basePath + baseName + fileExt
}

func RenameFile(afile string, newFileName string) error {
	return os.Rename(afile, newFileName)
}

func NewFileNameIfExistsThenIncSN(fileName string) string {
	rval := fileName
	sn := 0
	var basePath string
	var fileExt string
	var baseName string
	for {
		if !FileIsExists(rval) {
			return rval
		}

		if len(basePath) == 0 {
			basePath = ExtractFilePath(fileName) + "/"
			fileExt = path.Ext(fileName)
			baseName = ExtractFileNameWithOutPathAndExt(fileName)
		}
		sn++

		rval = basePath + baseName + fmt.Sprintf(".%d%s", sn, fileExt)
	}
}

func CreateIfExistsThenRename(name string) (*os.File, string, error) {
	itry := 0
	for {
		newFileName := NewFileNameIfExistsThenIncSN(name)

		f, err := os.Create(newFileName)
		if err != nil {
			if itry > 5 {
				return nil, "", err
			}
			itry++
			continue
		}
		return f, newFileName, nil
	}
}

func MD5SumFile(filePath string) string {
	const fileChunk = 8192 // 8KB
	file, err := os.Open(filePath)
	if err != nil {
		return err.Error()
	}
	defer file.Close()

	info, _ := file.Stat()
	fileSize := info.Size()

	blocks := uint64(math.Ceil(float64(fileSize) / float64(fileChunk)))
	hash := md5.New()

	for i := uint64(0); i < blocks; i++ {
		blockSize := int(math.Min(fileChunk, float64(fileSize-int64(i*fileChunk))))
		buf := make([]byte, blockSize)

		file.Read(buf)
		hash.Write(buf)
		//io.Write(hash, string(buf))
	}

	return fmt.Sprintf("%x", hash.Sum(nil))
}

type ReadFileSession struct {
	closeflag int8
	errflag   int8
	file      *os.File
	filename  string
}

func NewReadFileSession(filename string) *ReadFileSession {
	return &ReadFileSession{filename: filename}
}

func (this *ReadFileSession) Reset() {
	this.Close()
	this.file = nil
	this.closeflag = 0
	this.errflag = 0
}

func (this *ReadFileSession) Close() error {
	f0 := this.file
	if f0 != nil {
		return f0.Close()
	}
	this.closeflag = 1
	return io.ErrClosedPipe
}

func (this *ReadFileSession) CloseFlag() int8 {
	return this.closeflag
}

func (this *ReadFileSession) ErrFlag() int8 {
	return this.errflag
}

func (this *ReadFileSession) Read(b []byte) (n int, err error) {
	if this.file == nil {
		file, err := os.Open(this.filename)
		if err != nil {
			this.errflag = 1
			return -1, err
		}
		this.file = file
	}
	n, err = this.file.Read(b)
	if err != nil {
		this.errflag = 1
	}
	return
}

func ParseFileName(s string) string {
	return ReplacePlaceholder(s, "$(", ")", func(key string) (v string, ok bool) {
		if key == "pid" {
			return fmt.Sprintf("%d", os.Getpid()), true
		} else if key == "yyyymmdd" {
			return time.Now().Format("20060102"), true
		} else if key == "exename" {
			return ExtractFileNameWithOutPathAndExt(os.Args[0]), true
		} else if key1, ok1 := TryTrimPrefix(key, "env."); ok1 {
			return os.Getenv(key1), true
		}
		return GetDateTimeKeyValue(key, time.Now())
	})
}

// 如果path不为空, 确保尾部携带分隔符
func EnsurePathWithSlash(path string, sep string) string {
	if len(path) == 0 || (path[len(path)-1] != '/' && path[len(path)-1] != '\\') {
		path += sep
	}
	return path
}

// 统一路径中的分隔符
func UniformPathSep(f string, sep byte) string {
	data := []byte(f)
	for i := 0; i < len(data); i++ {
		if data[i] != sep && (data[i] == '/' || data[i] == '\\') {
			data[i] = sep
		}
	}
	return string(data)
}

// FileNameMatch 根据模式匹配文件名
//
//	testCases := []struct {
//		pattern  string
//		filename string
//		expected bool
//	}{
//		{"*", "中国/牛逼Glass.Abc", true},
//		{"中国/*", "中国/牛逼Glass.Abc", true},
//		{"中国/*.A", "中国/牛逼Glass.Abc", false},
//		{"中国/*.A?c", "中国/牛逼Glass.Abc", true},
//		{"?国/*.A?c", "中国/牛逼Glass.Abc", true},
//		{"中国/[中A].Abc", "中国/A.Abc", true},
//		{"中国/[!A].Abc", "中国/A.Abc", false},
//		{"中国/[!AB].Abc", "中国/C.Abc", true},
//		{"中国/｛A,中A｝.Abc", "中国/中A.Abc", true},
//		{"中国/｛A,中A｝.Abc", "中国/C.Abc", false},
//		{"中国/｛A,中B｝.Abc", "中国/中A.Abc", false},
//		{"*中国/｛A,中A｝.Abc", "地球/中国/C.Abc", false},
//		{"*中国/｛A,中A｝.Abc", "地球/中国/A.Abc", true},
//		{"*/中国/｛A,中A｝.Abc", "地球/中国/A.Abc", true},
//		{"??/中国/｛A,中A｝.Abc", "地球/中国/A.Abc", true},
//		{"*/中国/｛A,中A｝.Abc", "宇宙/地球/中国/A.Abc", true},
//	}
func FileNameMatch(pattern, filename string) bool {
	// 将模式转换为正则表达式
	regexPattern := convertToRegex(pattern)
	re, err := regexp.Compile(regexPattern)
	if err != nil {
		return false
	}
	return re.MatchString(filename)
}

// convertToRegex 将自定义模式转换为正则表达式
func convertToRegex(pattern string) string {
	// 先对特殊字符进行转义，除了 *、?、[、]、{、}、,、｛、｝
	escaped := ""
	for i, r := range pattern {
		switch r {
		case '*', '?', '[', ']', '{', '}', ',', '｛', '｝':
			if r == '[' && i+1 < len(pattern) && pattern[i+1] == '!' {
				escaped += "[^"
				i++
			} else {
				escaped += string(r)
			}
		default:
			escaped += regexp.QuoteMeta(string(r))
		}
	}
	// 处理全角大括号为半角大括号
	escaped = strings.ReplaceAll(escaped, "｛", "{")
	escaped = strings.ReplaceAll(escaped, "｝", "}")
	// 处理 * 匹配任意一个或多个字符
	escaped = strings.ReplaceAll(escaped, "*", ".*")
	// 处理?匹配单个字符
	escaped = strings.ReplaceAll(escaped, "?", ".")
	// 处理 {} 选项集
	escaped = strings.ReplaceAll(escaped, "{", "(")
	escaped = strings.ReplaceAll(escaped, "}", ")")
	escaped = strings.ReplaceAll(escaped, ",", "|")

	return "^" + escaped + "$"
}

func GetRelativeFileName(basePath, fileOrPath string, pathSep byte) string {
	if basePath == "" {
		return fileOrPath
	}

	if len(fileOrPath) == 0 {
		return fileOrPath
	}

	basePath = UniformPathSep(basePath, pathSep)
	fileOrPath = UniformPathSep(fileOrPath, pathSep)

	arr1 := strings.Split(basePath[1:], string([]byte{pathSep}))
	arr2 := strings.Split(fileOrPath[1:], string([]byte{pathSep}))
	depth := 0
	for i := 0; i < len(arr1) && i < len(arr2); i++ {
		if arr1[i] == arr2[i] {
			depth++
		} else {
			break
		}
	}
	prefix := ""
	if len(arr1)-depth-1 <= 0 {
		prefix = ""
	} else {
		for i := len(arr1) - depth - 1; i > 0; i-- {
			prefix += ".." + string(pathSep)
		}
	}
	if len(arr2)-depth > 0 {
		prefix += strings.Join(arr2[depth:], string(pathSep))
	}
	return prefix
}

func init() {
	if runtime.GOOS == "windows" {
		PathSeparator = "\\"
		cmd_exe_bin = "cmd"
	} else {
		if FileIsExists("/bin/bash") {
			cmd_exe_bin = "/bin/bash"
		} else {
			cmd_exe_bin = "/bin/sh"
		}
		PathSeparator = "/"
	}
	RootPath, _ = GetCurrentDirectory()
}
